#include <assert.h>
#include "TCPSession.h"
using namespace std;
static void _on_connect(uv_connect_t* conn, int status){
	CTCPSession* session = (CTCPSession*)conn->data;
	session->_OnConnectCb(conn, status);
}
static void _on_write(uv_write_t* req, int status)
{
	CTCPSession* session = (CTCPSession*)req->data;
	session->_OnWriteCb(req, status);
}

static void _on_close(uv_handle_t* handle) {
	CTCPSession* session = (CTCPSession*)handle->data;		
	session->_OnCloseCb();
}

static void _alloc_buf(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
	CTCPSession* session = (CTCPSession*)handle->data;
	buf->base = session->_rbuf.base;
	buf->len = session->_rbuf.len;
}


static void _on_rev(uv_stream_t* handle, ssize_t nread, const  uv_buf_t* buf)
{
	CTCPSession* session = (CTCPSession*)handle->data;
	session->_OnReadCb(handle, nread, buf);
}

static void _reconnect_cb(uv_timer_t *handle)
{
	CTCPSession* session = (CTCPSession*)handle->data;
	session->_ReConnect();
	free(handle);
}

CTCPSession::CTCPSession(uv_loop_t* loop)
{
	assert(loop);
	_rbuf.base = new  char[4096 * 10];
	_rbuf.len = 4096 * 10;
	_loop = loop;
}

bool CTCPSession::Connect(char* ip, size_t port)
{
	if (_bconn)return _bconn;	
	uv_tcp_init(_loop, &_socket);
	struct sockaddr_in dest;
	uv_ip4_addr(ip, port, &dest);
	_connect.data = this;
	uv_tcp_connect(&_connect, &_socket, (const sockaddr*)&dest, _on_connect);
	_perrip = string(ip);
	_perrport=port;
}
void CTCPSession::_AddReconnTimer()
{
	uv_timer_t*timer = (uv_timer_t*)malloc(sizeof(uv_timer_t));
	uv_timer_init(_loop, timer);
	timer->data = this;
	uv_timer_start(timer, _reconnect_cb, _interanl_ms, 0);
}
void CTCPSession::_OnCloseCb()
{	
	_bconn = false;
	if (_closecb){ _closecb(); }
	if (_interanl_ms > 0){_AddReconnTimer();}
};
void CTCPSession::_OnConnectCb(uv_connect_t* req, int status)
{
	_bconn = true;
	if (status < 0){
		_bconn = false;				
	}
	if (_connStatus){ _connStatus(status); }
	if (_interanl_ms>0){ _AddReconnTimer(); }
}


void CTCPSession::OnMsgCall(MsgCallBack msgcb)
{	
	_msgcb = msgcb;
	_connect.handle->data = this;
	uv_read_start(_connect.handle, _alloc_buf, _on_rev);
}

void CTCPSession::Write(char* buf, size_t bsize)
{
	uv_buf_t buffer;
	buffer.base = buf;
	buffer.len = bsize;
	uv_write_t*  write_req = (uv_write_t*)malloc(sizeof(uv_write_t));
	write_req->data = this;
	uv_write(write_req, _connect.handle, &buffer, 1, _on_write);
}


void CTCPSession::_OnReadCb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{
	if (nread < 0){
		handle->data = this;
		uv_close((uv_handle_t*)handle, _on_close);
		return;
	}
	if (_msgcb){ _msgcb(this->shared_from_this(), buf->base, nread); }
}

void CTCPSession::_OnWriteCb(uv_write_t* req, int status)
{
	uv_read_start(req->handle, _alloc_buf, _on_rev);
	free(req);
}

CTCPSession::~CTCPSession()
{
	delete _rbuf.base;	
	_rbuf.base=nullptr;
}

